use std::{fmt::Display, str::FromStr};

#[derive(Clone, PartialEq, Eq, Debug, Hash, Default)]
pub struct Family {
	pub name: String,
	pub style: Style,
	pub weight: Weight,
	pub stretch: Stretch,
}
impl<T: Into<String>> From<T> for Family {
	fn from(value: T) -> Self {
		Self {
			name: value.into(),
			style: Default::default(),
			weight: Default::default(),
			stretch: Default::default(),
		}
	}
}
impl Display for Family {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		write!(f, "{};{};{};{}", self.name, self.weight, self.style, self.stretch)
	}
}
impl FromStr for Family {
	type Err = &'static str;
	fn from_str(s: &str) -> Result<Self, Self::Err> {
		let mut lines = s.split(";");
		Ok(Self {
			name: lines.next().unwrap_or_default().parse().unwrap_or_default(),
			weight: lines.next().unwrap_or_default().parse().unwrap_or_default(),
			style: lines.next().unwrap_or_default().parse().unwrap_or_default(),
			stretch: lines.next().unwrap_or_default().parse().unwrap_or_default(),
		})
	}
}

#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum Style {
	Normal,
	Italic,
	Oblique,
}
impl Default for Style {
	#[inline]
	fn default() -> Style {
		Style::Normal
	}
}
impl FromStr for Style {
	type Err = &'static str;
	fn from_str(s: &str) -> Result<Self, Self::Err> {
		Ok(match s.trim() {
			"normal" => Self::Normal,
			"italic" => Self::Italic,
			"oblique" => Self::Oblique,
			_ => return Err("fail"),
		})
	}
}
impl Display for Style {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		let name = match self {
			Self::Normal => "normal",
			Self::Italic => "italic",
			Self::Oblique => "oblique",
		};
		write!(f, "{}", name)
	}
}

#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
pub struct Weight(pub u16);
impl Weight {
	pub const THIN: Weight = Weight(100);
	pub const EXTRA_LIGHT: Weight = Weight(200);
	pub const LIGHT: Weight = Weight(300);
	pub const NORMAL: Weight = Weight(400);
	pub const MEDIUM: Weight = Weight(500);
	pub const SEMIBOLD: Weight = Weight(600);
	pub const BOLD: Weight = Weight(700);
	pub const EXTRA_BOLD: Weight = Weight(800);
	pub const BLACK: Weight = Weight(900);
}
impl Default for Weight {
	#[inline]
	fn default() -> Weight {
		Weight::NORMAL
	}
}
impl FromStr for Weight {
	type Err = &'static str;
	fn from_str(s: &str) -> Result<Self, Self::Err> {
		Ok(match s.trim() {
			"thin" => Self::THIN,
			"extra-light" => Self::EXTRA_LIGHT,
			"light" => Self::LIGHT,
			"normal" => Self::NORMAL,
			"medium" => Self::MEDIUM,
			"semibold" => Self::SEMIBOLD,
			"bold" => Self::BOLD,
			"extra-bold" => Self::EXTRA_BOLD,
			"black" => Self::BLACK,
			_ => return Err("fail"),
		})
	}
}
impl Display for Weight {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		let name = match *self {
			Self::THIN => "thin",
			Self::EXTRA_LIGHT => "extra-light",
			Self::LIGHT => "light",
			Self::NORMAL => "normal",
			Self::MEDIUM => "medium",
			Self::SEMIBOLD => "semibold",
			Self::BOLD => "bold",
			Self::EXTRA_BOLD => "extra-bold",
			Self::BLACK => "black",
			_ => "unknown",
		};
		write!(f, "{}", name)
	}
}

#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
pub struct Stretch(pub u16);
impl Stretch {
	pub const ULTRA_CONDENSED: Stretch = Stretch(500);
	pub const EXTRA_CONDENSED: Stretch = Stretch(625);
	pub const CONDENSED: Stretch = Stretch(750);
	pub const SEMI_CONDENSED: Stretch = Stretch(875);
	pub const NORMAL: Stretch = Stretch(1000);
	pub const SEMI_EXPANDED: Stretch = Stretch(1125);
	pub const EXPANDED: Stretch = Stretch(1250);
	pub const EXTRA_EXPANDED: Stretch = Stretch(1500);
	pub const ULTRA_EXPANDED: Stretch = Stretch(2000);
}
impl From<f32> for Stretch {
	fn from(value: f32) -> Self {
		Self((value * 1000.0) as u16)
	}
}
impl From<Stretch> for f32 {
	fn from(value: Stretch) -> Self {
		value.0 as f32 / 1000.0
	}
}
impl Default for Stretch {
	#[inline]
	fn default() -> Self {
		Stretch::NORMAL
	}
}
impl FromStr for Stretch {
	type Err = &'static str;
	fn from_str(s: &str) -> Result<Self, Self::Err> {
		Ok(match s.trim() {
			"ultra-condensed" => Self::ULTRA_CONDENSED,
			"extra-condensed" => Self::EXTRA_CONDENSED,
			"condensed" => Self::CONDENSED,
			"semi-condensed" => Self::SEMI_CONDENSED,
			"normal" => Self::NORMAL,
			"semi-expanded" => Self::SEMI_EXPANDED,
			"expanded" => Self::EXPANDED,
			"extra-expanded" => Self::EXTRA_EXPANDED,
			"ultra-expanded" => Self::ULTRA_EXPANDED,
			_ => return Err("fail"),
		})
	}
}
impl Display for Stretch {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		let name = match *self {
			Self::ULTRA_CONDENSED => "ultra-condensed",
			Self::EXTRA_CONDENSED => "extra-condensed",
			Self::CONDENSED => "condensed",
			Self::SEMI_CONDENSED => "semi-condensed",
			Self::NORMAL => "normal",
			Self::SEMI_EXPANDED => "semi-condensed",
			Self::EXPANDED => "expanded",
			Self::EXTRA_EXPANDED => "extra-condensed",
			Self::ULTRA_EXPANDED => "ultra-condensed",
			_ => "unknown",
		};
		write!(f, "{}", name)
	}
}
